#ifndef		__SCRIPT_H_INCLUDED__
#define		__SCRIPT_H_INCLUDED__

extern "C" {
#	include	"lua/lua.h"
#	include "lua/lualib.h"
#	include	"lua/lauxlib.h"
}

#include	<cstring>
#include	<string>

#define		LUA(code)	#code
#define		GLua		LuaVM::Instance()

/**
 * Template helpers.
 */
template<typename T> struct LuaGetter {};
template<typename T> struct LuaPusher {};
template<typename T> struct LuaName { static const char * Of() { return "unknown"; } };
template<typename T> struct LuaType { static bool Is(int) { return false; }; };

/**
 * Macro for declare enum in Lua
 */
#define		LUA_ENUM(T)	\
template<> struct LuaGetter<T> { static T Do(lua_State * p, int n) { return (T)lua_tointeger(p, n); } }; \
template<> struct LuaPusher<T> { static void Do(lua_State * p, T t) { lua_pushinteger(p, (lua_Integer)t); } }; \
template<> struct LuaName<T> { static const char * Of() { return "integer"; } }; \
template<> struct LuaType<T> { static bool Is(int n) { return n == LUA_TNUMBER; } };

/**
 * Lua api type
 */
typedef int (*LuaApi)(class LuaStack &);

/**
 * Lua nil type
 */
class LuaNil {};

/**
 * Lua Table.
 */
class LuaTable {
public:
	LuaTable(lua_State * L, int ref) : _L(L), _ref(ref) {}
	LuaTable(const LuaTable & r) : _L(r._L), _ref(LUA_NOREF) { lua_rawgeti(_L, LUA_REGISTRYINDEX, r._ref); _ref = luaL_ref(_L, LUA_REGISTRYINDEX); }
	virtual ~LuaTable() { __Unref(); }

	/** Copy statement */
	LuaTable & operator=(const LuaTable & r);

	/** Is this table valid? */
	inline bool IsValid() const { return (_L && _ref != LUA_NOREF); }

	/** Push self on top of current stack */
	void Push() { if (IsValid()) lua_rawgeti(_L, LUA_REGISTRYINDEX, _ref); else lua_pushnil(_L); }

	/** Get size of this table. Equal with '#' operator in Lua. */
	int Size() const;

	/**
	 * Is element convertible with given type?
	 *
	 * \param idx Index of this element in array table.
	 * \return Value of element in this table is convertible with given type?
	 */
	template<typename T>
	bool Is(int idx);

	/**
	 * Is element convertible with given type?
	 *
	 * \param key Key to find this element in map table.
	 * \return True for element is convertible with given type.
	 */
	template<typename T>
	bool Is(const std::string & key);

	/**
	 * Get element table[idx]
	 * A std::runtime_error will be thrown when the value of element is not convertible
	 * with given type. You should use try...catch statement to avoid crash except using
	 * this method in a registered C++ function.
	 *
	 * \param idx Element index.
	 * \return Value of this element.
	 */
	template<typename T>
	T Get(int idx);

	/**
	 * Get element table[key]
	 * A std::runtime_error will be thrown when the value of element is not convertible
	 * with given type. You should use try...catch statement to avoid crash except using
	 * this method in a registered C++ function.
	 *
	 * \param key Key to find this element.
	 * \return Value of this element.
	 */
	template<typename T>
	T Get(const std::string & key);

	/**
	 * More safety get method than LuaTable::Get<T>(int idx).
	 *
	 * \param idx Element index.
	 * \param out If element's type is convertible with given type, stores it's value.
	 * \return Is value of element convertible with given type.
	 */
	template<typename T>
	bool TryGet(int idx, T & out);
	
	/**
	 * More safety get method than LuaTable::Get<T>(const std::string & key).
	 *
	 * \param idx Element index.
	 * \param out If element's type is convertible with given type, stores it's value.
	 * \return Is value of element convertible with given type.
	 */
	template<typename T>
	bool TryGet(const std::string & key, T & out);

	/**
	 * Modify table's element. Equals to table[idx] = t
	 *
	 * \param idx Element's index in this array table.
	 * \param value New value for this element.
	 */
	template<typename T>
	void Set(int idx, T value);

	/**
	 * Modify table's element. Equals to table[key] = t
	 *
	 * \param key Element's key in this map table.
	 * \param value New value for this element.
	 */
	template<typename T>
	void Set(const std::string & key, T value);

private:
	void __Unref();

protected:
	lua_State * _L;
	int _ref;
};

/**
 * Lua stack
 */
class LuaStack {
public:
	LuaStack(lua_State * L, int clean) : _L(L), _clean(clean) {}
	virtual ~LuaStack() { if (_clean > 0) lua_pop(_L, _clean); }

	/** Get top of current stack. */
	inline int Top() { return lua_gettop(_L); }

	/**
	 * Pop number of values from current stack.
	 *
	 * \param n Number of elements to pop.
	 */
	inline void Pop(int n) { lua_pop(_L, n); }

	/**
	 * Create new table. NOTE: this table is not on top of current stack! You can manually call
	 * LuaTable::Push() or LuaStack::Push<LuaTable>();
	 */
	inline LuaTable NewTable() { lua_newtable(_L); return LuaTable(_L, luaL_ref(_L, LUA_REGISTRYINDEX)); }

	/**
	 * Get type of stack element.
	 *
	 * \param idx Index of this element on this stack.
	 * \return Type of this element.
	 */
	inline int Type(int idx) { return lua_type(_L, idx); }

	/**
	 * Is element on stack convertible with given type?
	 *
	 * \param idx Index of this element on this stack.
	 * \return If this element is convertible with T returns true. Otherwise false will be returned.
	 */
	template<typename T>
	bool Is(int idx);

	/**
	 * Convert Lua element on stack to C++ value.
	 * A std::runtime_error will be thrown when the value of element is not convertible
	 * with given type. You should use try...catch statement to avoid crash except using
	 * this method in a registered C++ function.
	 *
	 * \param idx Index of this element on this stack.
	 * \return Value of element with given type.
	 */
	template<typename T>
	T Get(int idx);

	/**
	 * Push value onto this stack.
	 *
	 * \param value Value to push on to stack.
	 */
	template<typename T>
	void Push(T value);

private:
	lua_State * _L;
	int _clean;
};

/**
 * Helper class for exporting static properties or methods.
 */
class LuaNamespace : public LuaTable {
public:
	LuaNamespace(lua_State * L, int ref) : LuaTable(L, ref) {}

	/**
	 * Register property by value's address.
	 *
	 * \param name Key to find this property.
	 * \param ptr Pointer address of this property.
	 * \param read_only Is this property read-only?
	 * \return Self to code consistently.
	 */
	template<typename T>
	LuaNamespace & Property(const std::string & name, T * ptr, bool read_only = true);

	/**
	 * Register readonly property by getter function.
	 *
	 * \param name Property's key in Lua
	 * \param getter Getter function.
	 * \return Self to code consistently.
	 */
	template<typename T>
	LuaNamespace & Property(const std::string & name, T(*getter)()) {
		return Property<T, T>(name, getter, nullptr);
	}

	/**
	 * Register property by getter and setter function.
	 *
	 * \param name Key to find this property.
	 * \param getter Getter function.
	 * \param setter Setter function.
	 * \return Self to code consistently.
	 */
	template<typename TG, typename TS>
	LuaNamespace & Property(const std::string & name, TG (*getter)(), void (*setter)(TS));

	/**
	 * Register method into this namespace(table).
	 *
	 * \param name Method name used in Lua.
	 * \param func C++ function to call.
	 * \return Self to code consistently.
	 */
	LuaNamespace & Method(const std::string & name, LuaApi func);
};

/**
 * Helper class for exporting C++ class to Lua layer.
 */
template<class O>
class LuaClass : public LuaTable {
public:
	LuaClass(lua_State * L, int ref) : LuaTable(L, ref) {}

	/**
	 * Register property by member's address.
	 *
	 * \param name Key to find this property.
	 * \param ptr Pointer address of this property.
	 * \param read_only Is this property read-only?
	 * \return Self to code consistently.
	 */
	template<typename T>
	LuaClass<O> & Property(const std::string & name, T O::*ptr, bool read_only = true);

	/**
	 * Register readonly property by getter.
	 *
	 * \param name Property's key in Lua
	 * \param getter Getter function.
	 * \return Self to code consistently
	 */
	template<typename T>
	LuaClass<O> & Property(const std::string & name, T(O::*getter)()) {
		return Property<T, T>(name, getter, nullptr);
	}

	/**
	 * Register readonly property by getter.
	 *
	 * \param name Property's key in Lua
	 * \param getter Getter function.
	 * \return Self to code consistently
	 */
	template<typename T>
	LuaClass<O> & Property(const std::string & name, T(O::*getter)() const) {
		return Property<T, T>(name, getter, nullptr);
	}

	/**
	 * Register property by getter and setter function.
	 *
	 * \param name Key to find this property.
	 * \param getter Getter function.
	 * \param setter Setter function.
	 * \return Self to code consistently.
	 */
	template<typename TG, typename TS>
	LuaClass<O> & Property(const std::string & name, TG (O::*getter)(), void (O::*setter)(TS));

	/**
	 * Register property by getter and setter function.
	 *
	 * \param name Key to find this property.
	 * \param getter Getter function const.
	 * \param setter Setter function.
	 * \return Self to code consistently.
	 */
	template<typename TG, typename TS>
	LuaClass<O> & Property(const std::string & name, TG (O::*getter)() const, void (O::*setter)(TS));

	/**
	 * Register method into this class(table).
	 *
	 * \param name Method name used in Lua.
	 * \param func C++ function to call.
	 * \return Self to code consistently.
	 */
	LuaClass<O> & Method(const std::string & name, LuaApi func);

	/**
	 * Register static method for class
	 * 
	 * \param name Method name
	 * \param func C++ function to call.
	 * \return Self to code consistently.
	 */
	LuaClass<O> & StaticMethod(const std::string & name, LuaApi func);
};

/**
 * Lua VM. High level interface for Lua.
 */
class LuaVM {
public:
	LuaVM();
	virtual ~LuaVM() { if (_L) lua_close(_L); }

	/** Get runtime singleton instance of Lua Virtual Machine. */
	static LuaVM &	Instance();

	/** Get low level interface of Lua VM. */
	inline lua_State * State() { return _L; }

	/** Get current stack of this Lua VM. */
	inline LuaStack	Stack() { return LuaStack(_L, 0); }

	/** Run lua code */
	void Run(const std::string & code);

	/**
	 * Load and run lua script.
	 *
	 * \param path Lua script file path.
	 * \param require Load this file in 'require' mode?
	 */
	void DoFile(const std::string & path, bool require = false);

	/**
	 * Is global variable convertible with given type?
	 *
	 * \param name Variable's name.
	 * \return True for it is convertible.
	 */
	template<typename T>
	bool Is(const std::string & name);

	/**
	 * Get global variable's value.
	 * A std::runtime_error will be thrown when the value of element is not convertible
	 * with given type. You should use try...catch statement to avoid crash except using
	 * this method in a registered C++ function.
	 *
	 * \param name Variable's name.
	 * \return Variable's value.
	 */
	template<typename T>
	T Get(const std::string & name);

	/**
	 * More safety get method than LuaVM::Get<T>(const std::string & name)
	 *
	 * \param name Name to find this variable.
	 * \param out If element's type is wanted, stores it's value.
	 * \return True for element is convertible with given type.
	 */
	template<typename T>
	bool TryGet(const std::string & name, T & out);

	/**
	 * Modify global variable.
	 *
	 * \param name Variable's name.
	 * \param value Variable's value.
	 */
	template<typename T>
	void Set(const std::string & name, T t);

	/**
	 * Call function defined in Lua.
	 *
	 * \param table Name of table that contains this function. Can be global '_G'.
	 * \param func Name of method to call.
	 * \param use_self Using table:func(args...) instead of table.func(args...)
	 * \param args... Parameters.
	 * \return Result stack.
	 */
	template<typename ... Args>
	LuaStack Call(const std::string & table, const std::string & func, bool use_self, Args && ... args);

	/**
	 * Create toolset for exporting static properties and methods.
	 *
	 * \param table_name Name of this namespace(table).
	 * \return Register toolset.
	 */
	LuaNamespace Register(const std::string & table_name);

	/**
	 * Create toolset for exporting C++ class.
	 *
	 * \param class_name Class name to extends.
	 * \return Register toolset.
	 */
	template<class O>
	LuaClass<O> Register(const std::string & class_name);

	/**
	 * Create toolset for exporting C++ class with relationship.
	 *
	 * \param class_name Class name to extends.
	 * \return Register to add properties and methods.
	 */
	template<class Derived, class Base>
	LuaClass<Derived> Register(const std::string & class_name);

private:
	LuaVM(const LuaVM &) = delete;
	LuaVM & operator=(const LuaVM &) = delete;

private:
	lua_State * _L;
};

/**
 * Lua scope guard helper
 */
template<class O>
class LuaGuard {
public:
	/// Store the old one & Set _G[key] = new one.
	LuaGuard(const std::string & key, O * p) : _key(key), _p(nullptr) {
		if (GLua.Is<O *>(key)) _p = GLua.Get<O *>(key);
		GLua.Set<O *>(key, p);
	}

	/// Restore the old one. _G[key] = old one.
	virtual ~LuaGuard() { GLua.Set<O *>(_key, _p); }

private:
	std::string	_key;
	O *			_p;
};

///////////////////////////////////////////////////////////////////////////////
/// LuaCleanType
///////////////////////////////////////////////////////////////////////////////
template<typename T>
struct LuaCleanType {
	typedef typename std::remove_const<typename std::remove_reference<T>::type>::type Type;
};

///////////////////////////////////////////////////////////////////////////////
/// LuaClassInfo
///////////////////////////////////////////////////////////////////////////////
template<class O> struct LuaClassInfo {
	static std::string	Name;
	static bool			Used;
	static int			RefIdx;
};
template<typename O> std::string LuaClassInfo<O>::Name = "Unknown";
template<typename O> bool LuaClassInfo<O>::Used = false;
template<typename O> int LuaClassInfo<O>::RefIdx = LUA_NOREF;

////////////////////////////////////////////////////////////////////////////////
/// LuaGetter
////////////////////////////////////////////////////////////////////////////////
template<typename T> struct LuaGetter<T *> { static T * Do(lua_State * p, int n) { return *((T **)lua_touserdata(p, n)); } };
template<> struct LuaGetter<bool> { static bool Do(lua_State * p, int n) { return 1 == lua_toboolean(p, n); } };
template<> struct LuaGetter<int8_t> { static int8_t Do(lua_State * p, int n) { return lua_tointeger(p, n) & 0xFF; } };
template<> struct LuaGetter<uint8_t> { static uint8_t Do(lua_State * p, int n) { return lua_tointeger(p, n) & 0xFF; } };
template<> struct LuaGetter<int16_t> { static int16_t Do(lua_State * p, int n) { return lua_tointeger(p, n) & 0xFFFF; } };
template<> struct LuaGetter<uint16_t> { static uint16_t Do(lua_State * p, int n) { return lua_tointeger(p, n) & 0xFFFF; } };
template<> struct LuaGetter<int32_t> { static int32_t Do(lua_State * p, int n) { return lua_tointeger(p, n) & 0xFFFFFFFF; } };
template<> struct LuaGetter<uint32_t> { static uint32_t Do(lua_State * p, int n) { return lua_tointeger(p, n) & 0xFFFFFFFF; } };
template<> struct LuaGetter<int64_t> { static int64_t Do(lua_State * p, int n) { return (int64_t)lua_tointeger(p, n); } };
template<> struct LuaGetter<uint64_t> { static uint64_t Do(lua_State * p, int n) { return (uint64_t)lua_tointeger(p, n); } };
template<> struct LuaGetter<float> { static float Do(lua_State * p, int n) { return (float)lua_tonumber(p, n); } };
template<> struct LuaGetter<double> { static double Do(lua_State * p, int n) { return lua_tonumber(p, n); } };
template<> struct LuaGetter<char *> { static const char * Do(lua_State * p, int n) { return lua_tostring(p, n); } };
template<> struct LuaGetter<std::string> { static std::string Do(lua_State * p, int n) { size_t l; const char * s = lua_tolstring(p, n, &l); return std::string(s, l); } };
template<> struct LuaGetter<LuaNil> { static LuaNil Do(lua_State * p, int n) { return LuaNil(); } };
template<> struct LuaGetter<LuaTable> { static LuaTable Do(lua_State * p, int n) { lua_pushvalue(p, n); return LuaTable(p, luaL_ref(p, LUA_REGISTRYINDEX)); } };

////////////////////////////////////////////////////////////////////////////////
/// LuaPusher
////////////////////////////////////////////////////////////////////////////////
template<> struct LuaPusher<bool> { static void Do(lua_State * p, bool b) { lua_pushboolean(p, b ? 1 : 0); } };
template<> struct LuaPusher<int8_t> { static void Do(lua_State * p, int8_t n) { lua_pushinteger(p, (lua_Integer)n); } };
template<> struct LuaPusher<uint8_t> { static void Do(lua_State * p, uint8_t n) { lua_pushinteger(p, (lua_Integer)n); } };
template<> struct LuaPusher<int16_t> { static void Do(lua_State * p, int16_t n) { lua_pushinteger(p, (lua_Integer)n); } };
template<> struct LuaPusher<uint16_t> { static void Do(lua_State * p, uint16_t n) { lua_pushinteger(p, (lua_Integer)n); } };
template<> struct LuaPusher<int32_t> { static void Do(lua_State * p, int32_t n) { lua_pushinteger(p, (lua_Integer)n); } };
template<> struct LuaPusher<uint32_t> { static void Do(lua_State * p, uint32_t n) { lua_pushinteger(p, (lua_Integer)n); } };
template<> struct LuaPusher<int64_t> { static void Do(lua_State * p, int64_t n) { lua_pushinteger(p, (lua_Integer)n); } };
template<> struct LuaPusher<uint64_t> { static void Do(lua_State * p, uint64_t n) { lua_pushinteger(p, (lua_Integer)n); } };
template<> struct LuaPusher<float> { static void Do(lua_State * p, float n) { lua_pushnumber(p, (lua_Number)n); } };
template<> struct LuaPusher<double> { static void Do(lua_State * p, double n) { lua_pushnumber(p, n); } };
template<> struct LuaPusher<char *> { static void Do(lua_State * p, const char * s) { lua_pushstring(p, s); } };
template<> struct LuaPusher<std::string> { static void Do(lua_State * p, const std::string & s) { lua_pushlstring(p, s.data(), s.size()); } };
template<> struct LuaPusher<LuaNil> { static void Do(lua_State * p, LuaNil & r) { lua_pushnil(p); } };
template<> struct LuaPusher<LuaTable> { static void Do(lua_State * p, LuaTable & r) { r.Push(); } };
template<class T> struct LuaPusher<T *> {
	static void Do(lua_State * p, T * u) {
		if (u == nullptr) {
			lua_pushnil(p);
			return;
		}

		T ** ptr = static_cast<T **>(lua_newuserdata(p, sizeof(T *)));
		*ptr = u;
		luaL_getmetatable(p, LuaClassInfo<T>::Name.c_str());
		if (lua_isnil(p, -1)) luaL_error(p, "Class %s NOT registered!!!", LuaClassInfo<T>::Name.c_str());
		lua_setmetatable(p, -2);
	}
};

//////////////////////////////////////////////////////////////////////////
/// LuaName
//////////////////////////////////////////////////////////////////////////
template<typename T> struct LuaName<T *> { static const char * Of() { return LuaClassInfo<T>::Used ? LuaClassInfo<T>::Name.c_str() : "userdata"; } };
template<> struct LuaName<bool> { static const char * Of() { return "boolean"; } };
template<> struct LuaName<int8_t> { static const char * Of() { return "integer"; } };
template<> struct LuaName<uint8_t> { static const char * Of() { return "integer"; } };
template<> struct LuaName<int16_t> { static const char * Of() { return "integer"; } };
template<> struct LuaName<uint16_t> { static const char * Of() { return "integer"; } };
template<> struct LuaName<int32_t> { static const char * Of() { return "integer"; } };
template<> struct LuaName<uint32_t> { static const char * Of() { return "integer"; } };
template<> struct LuaName<int64_t> { static const char * Of() { return "integer"; } };
template<> struct LuaName<uint64_t> { static const char * Of() { return "integer"; } };
template<> struct LuaName<float> { static const char * Of() { return "number"; } };
template<> struct LuaName<double> { static const char * Of() { return "number"; } };
template<> struct LuaName<char *> { static const char * Of() { return "string"; } };
template<> struct LuaName<std::string> { static const char * Of() { return "string"; } };
template<> struct LuaName<LuaNil> { static const char * Of() { return "nil"; } };
template<> struct LuaName<LuaTable> { static const char * Of() { return "table"; } };

//////////////////////////////////////////////////////////////////////////
/// LuaType
//////////////////////////////////////////////////////////////////////////
template<typename T> struct LuaType<T *> { static bool Is(int n) { return n == LUA_TUSERDATA || n == LUA_TLIGHTUSERDATA; } };
template<> struct LuaType<bool> { static bool Is(int n) { return n == LUA_TBOOLEAN; } };
template<> struct LuaType<int8_t> { static bool Is(int n) { return n == LUA_TNUMBER; } };
template<> struct LuaType<uint8_t> { static bool Is(int n) { return n == LUA_TNUMBER; } };
template<> struct LuaType<int16_t> { static bool Is(int n) { return n == LUA_TNUMBER; } };
template<> struct LuaType<uint16_t> { static bool Is(int n) { return n == LUA_TNUMBER; } };
template<> struct LuaType<int32_t> { static bool Is(int n) { return n == LUA_TNUMBER; } };
template<> struct LuaType<uint32_t> { static bool Is(int n) { return n == LUA_TNUMBER; } };
template<> struct LuaType<int64_t> { static bool Is(int n) { return n == LUA_TNUMBER; } };
template<> struct LuaType<uint64_t> { static bool Is(int n) { return n == LUA_TNUMBER; } };
template<> struct LuaType<float> { static bool Is(int n) { return n == LUA_TNUMBER; } };
template<> struct LuaType<double> { static bool Is(int n) { return n == LUA_TNUMBER; } };
template<> struct LuaType<char *> { static bool Is(int n) { return n == LUA_TSTRING; } };
template<> struct LuaType<std::string> { static bool Is(int n) { return n == LUA_TSTRING; } };
template<> struct LuaType<LuaNil> { static bool Is(int n) { return n == LUA_TNIL; } };
template<> struct LuaType<LuaTable> { static bool Is(int n) { return n == LUA_TTABLE; } };

//////////////////////////////////////////////////////////////////////////
/// LuaMultiPusher
//////////////////////////////////////////////////////////////////////////
struct LuaMultiPusher {
	template<typename Head, typename ... Tail>
	static void Do(lua_State * p, Head && h, Tail && ... tail) {
		LuaPusher<typename LuaCleanType<Head>::Type>::Do(p, h);
		Do(p, tail...);
	}

	template<typename Head>
	static void Do(lua_State * p, Head && h) {
		LuaPusher<typename LuaCleanType<Head>::Type>::Do(p, h);
	}

	static void Do(lua_State * p) {}
};

//////////////////////////////////////////////////////////////////////////
/// LuaProxy
//////////////////////////////////////////////////////////////////////////
struct LuaProxy {
	static int	Readonly(lua_State * L);
	static int	Index(lua_State * L);
	static int	NewIndex(lua_State * L);
	static int	Method(lua_State * L);

	template<typename T>
	static int Get(lua_State * L) {
		if (!lua_islightuserdata(L, lua_upvalueindex(1))) luaL_error(L, "Get property by address error!");

		T const * ptr = (T const *)(lua_touserdata(L, lua_upvalueindex(1)));
		if (!ptr) luaL_error(L, "Get property by address error. Pointer missing!!!");

		LuaPusher<typename LuaCleanType<T>::Type>::Do(L, *ptr);
		return 1;
	}

	template<typename T>
	static int Set(lua_State * L) {
		if (!lua_islightuserdata(L, lua_upvalueindex(1))) luaL_error(L, "Set property by address error!");

		T const * ptr = (T const *)(lua_touserdata(L, lua_upvalueindex(1)));
		if (!ptr) luaL_error(L, "Set property by address error. Pointer missing!!!");

		*ptr = LuaGetter<typename LuaCleanType<T>::Type>::Do(L, 1);
		return 0;
	}

	template<typename T>
	static int Getter(lua_State * L) {
		if (!lua_isuserdata(L, lua_upvalueindex(1))) luaL_error(L, "Get property by function error!");

		typedef T(*GetterFunc)();
		GetterFunc * f = (GetterFunc *)(lua_touserdata(L, lua_upvalueindex(1)));
		if (!f) luaL_error(L, "Get property by function error. Function missing!!!");

		LuaPusher<typename LuaCleanType<T>::Type>::Do(L, (*f)());
		return 1;
	}

	template<typename T>
	static int Setter(lua_State * L) {
		if (!lua_isuserdata(L, lua_upvalueindex(1))) luaL_error(L, "Set property by function error!");

		typedef void(*SetterFunc)(T);
		SetterFunc * f = (SetterFunc *)(lua_touserdata(L, lua_upvalueindex(1)));
		if (!f) luaL_error(L, "Set property by function error. Function missing!!!");

		(*f)(LuaGetter<typename LuaCleanType<T>::Type>::Do(L, 1));
		return 0;
	}

	template<class O>
	static int ClassIndex(lua_State * L) {
		if (!lua_isuserdata(L, 1)) return 0;

		lua_getmetatable(L, 1); // tb, key, meta
		while (true) {
			lua_pushvalue(L, 2);	// tb, key, meta, key
			lua_rawget(L, -2);		// tb, key, meta, meta[key]

			if (lua_iscfunction(L, -1) || lua_isfunction(L, -1)) {
				lua_remove(L, -2);	// tb, key, meta[key]
				return 1;
			}

			if (!lua_isnil(L, -1)) {
				int n = lua_type(L, -1);
				lua_pop(L, 2);		// tb, key
				luaL_error(L, "Index of '%s' of class '%s' must be a function but current is '%s'", lua_tostring(L, 2), LuaName<O *>::Of(), lua_typename(L, n));
			}

			lua_pop(L, 1);					// tb, key, meta
			lua_pushstring(L, "__propget");	// tb, key, meta, '__propget'
			lua_rawget(L, -2);				// tb, key, meta, meta.__propget

			if (!lua_istable(L, -1)) {
				lua_pop(L, 2);
				luaL_error(L, "Class '%s' missing '__propget' in metatable!!!", LuaName<O *>::Of());
			}

			lua_pushvalue(L, 2);	// tb, key, meta, meta.__propget, key
			lua_rawget(L, -2);		// tb, key, meta, meta.__propget, meta.__propget[key]
			lua_remove(L, -2);		// tb, key, meta, meta.__propget[key]

			if (lua_iscfunction(L, -1)) {
				lua_pushvalue(L, 1);	// tb, key, meta, meta.__propget[key], tb
				lua_remove(L, -3);		// tb, key, meta.__propget[key], tb
				lua_call(L, 1, 1);
				return 1;
			}

			if (lua_isnil(L, -1)) {
				lua_pop(L, 1);					// tb, key, meta
				lua_pushstring(L, "__parent");	// tb, key, meta, '__parent'
				lua_rawget(L, -2);				// tb, key, meta, meta.__parent
				lua_remove(L, -2);				// tb, key, meta.__parent

				if (lua_istable(L, -1)) continue;
				if (lua_isnil(L, -1)) return 1;

				lua_pop(L, 1);
				lua_pushnil(L);
				return 1;
			} else {
				lua_pop(L, 2);
				luaL_error(L, "Class '%s' bad data in metatable.__propget[%s]!!!", LuaName<O *>::Of(), lua_tostring(L, 2));
			}			
		}
	}

	template<class O>
	static int ClassNewIndex(lua_State * L) {
		if (!lua_isuserdata(L, 1)) return 0;

		lua_getmetatable(L, 1); // tb, k, v, meta
		while (true) {
			lua_pushstring(L, "__propset");	// tb, k, v, meta, '__propset'
			lua_rawget(L, -2);				// tb, k, v, meta, meta.__propset

			if (!lua_istable(L, -1)) {
				lua_pop(L, 2);
				luaL_error(L, "Class '%s' missing metatable.__propset!!!", LuaName<O *>::Of());
			}

			lua_pushvalue(L, 2);	// tb, k, v, meta, meta.__propset, k
			lua_rawget(L, -2);		// tb, k, v, meta, meta.__propset, meta.__propset[k]
			lua_remove(L, -2);		// tb, k, v, meta, meta.__propset[k]

			if (lua_isfunction(L, -1)) {
				lua_remove(L, -2);		// tb, k, v, meta.__propset[k]
				lua_pushvalue(L, 1);	// tb, k, v, meta.__propset[k], tb
				lua_pushvalue(L, 3);	// tb, k, v, meta.__propset[k], tb, v
				lua_call(L, 2, 0);
				break;
			}

			if (lua_isnil(L, -1)) {
				lua_pop(L, 1);					// tb, k, v, meta
				lua_pushstring(L, "__parent");	// tb, k, v, meta, '__parent'
				lua_rawget(L, -2);				// tb, k, v, meta, meta.__parent
				lua_remove(L, -2);				// tb, k, v, meta.__parent

				if (!lua_istable(L, -1)) {
					lua_pop(L, 1);
					luaL_error(L, "Try to set '%s'.'%s' which is not registed!!!", LuaName<O *>::Of(), lua_tostring(L, 2));
				}
			}
		}

		return 0;
	}

	template<class O>
	static int ClassToString(lua_State * L) {
		O ** p = (O **)lua_touserdata(L, 1);
		lua_pushfstring(L, "%s[%p]", LuaName<O *>::Of(), *p);
		return 1;
	}

	template<class O>
	static int ClassMethod(lua_State * L) {
		if (lua_gettop(L) < 1) luaL_error(L, "Class '%s' call function error syntax. Please use ':' instead of '.'!", LuaName<O *>::Of());
		if (!lua_isuserdata(L, lua_upvalueindex(1))) luaL_error(L, "Class '%s' call function error!", LuaName<O *>::Of());

		typedef int(*ClassExternalFunc)(LuaStack &);

		ClassExternalFunc f = (ClassExternalFunc)(lua_touserdata(L, lua_upvalueindex(1)));
		if (!f) luaL_error(L, "Class '%s' call function error! nil", LuaName<O *>::Of());

		LuaStack _(L, 0);
		return f(_);
	}

	template<class O, typename T>
	static int ClassGet(lua_State * L) {
		if (!lua_isuserdata(L, lua_upvalueindex(1))) luaL_error(L, "Class '%s' get property by address error!", LuaName<O *>::Of());

		O ** p = (O **)lua_touserdata(L, 1);
		T O::** ptr = (T O::**)lua_touserdata(L, lua_upvalueindex(1));
		if (!ptr) luaL_error(L, "Class '%s' get property by address error! Missing pointer address", LuaName<O *>::Of());

		LuaPusher<typename LuaCleanType<T>::Type>::Do(L, (*p)->**ptr);
		return 1;
	}

	template<class O, typename T>
	static int ClassSet(lua_State * L) {
		if (!lua_isuserdata(L, lua_upvalueindex(1))) luaL_error(L, "Class '%s' set property by address error!", LuaName<O *>::Of());

		O ** p = (O **)lua_touserdata(L, 1);
		T O::** ptr = (T O::**)lua_touserdata(L, lua_upvalueindex(1));
		if (!ptr) luaL_error(L, "Class '%s' set property by address error! Missing pointer address", LuaName<O *>::Of());

		(*p)->**ptr = LuaGetter<typename LuaCleanType<T>::Type>::Do(L, 2);
		return 0;
	}

	template<class O, typename T>
	static int ClassGetter(lua_State * L) {
		if (!lua_isuserdata(L, lua_upvalueindex(1))) luaL_error(L, "Class '%s' get property by function error!", LuaName<O *>::Of());

		typedef T(O::*ClassGetFunc)();

		O ** p = (O **)lua_touserdata(L, 1);
		ClassGetFunc * f = (ClassGetFunc *)lua_touserdata(L, lua_upvalueindex(1));
		if (!f) luaL_error(L, "Class '%s' get property by function error! nil", LuaName<O *>::Of());

		LuaPusher<typename LuaCleanType<T>::Type>::Do(L, ((*p)->**f)());
		return 1;
	}

	template<class O, typename T>
	static int ClassConstGetter(lua_State * L) {
		if (!lua_isuserdata(L, lua_upvalueindex(1))) luaL_error(L, "Class '%s' get property by function error!", LuaName<O *>::Of());

		typedef T(O::*ClassConstGetFunc)() const;

		O ** p = (O **)lua_touserdata(L, 1);
		ClassConstGetFunc * f = (ClassConstGetFunc *)lua_touserdata(L, lua_upvalueindex(1));
		if (!f) luaL_error(L, "Class '%s' get property by function error! nil", LuaName<O *>::Of());

		LuaPusher<typename LuaCleanType<T>::Type>::Do(L, ((*p)->**f)());
		return 1;
	}

	template<class O, typename T>
	static int ClassSetter(lua_State * L) {
		if (!lua_isuserdata(L, lua_upvalueindex(1))) luaL_error(L, "Class '%s' set property by function error!", LuaName<O *>::Of());

		typedef void(O::*ClassSetFunc)(T);

		O ** p = (O **)lua_touserdata(L, 1);
		ClassSetFunc * f = (ClassSetFunc *)lua_touserdata(L, lua_upvalueindex(1));
		if (!f) luaL_error(L, "Class '%s' set property by function error! nil", LuaName<O *>::Of());

		((*p)->**f)(LuaGetter<typename LuaCleanType<T>::Type>::Do(L, 2));
		return 0;
	}
};

template<> struct LuaPusher<LuaApi> {
	static void Do(lua_State * p, LuaApi f) {
		lua_pushlightuserdata(p, (void *)f);
		lua_pushcclosure(p, &LuaProxy::Method, 1);
	}
};

//////////////////////////////////////////////////////////////////////////
/// LuaStack
//////////////////////////////////////////////////////////////////////////
template<typename T>
bool LuaStack::Is(int idx) {
	return LuaType<typename LuaCleanType<T>::Type>::Is(lua_type(_L, idx));
}

template<typename T>
T LuaStack::Get(int idx) {
	typedef typename LuaCleanType<T>::Type T0;
	int n = lua_type(_L, idx);
	if (!LuaType<T0>::Is(n)) luaL_error(_L, "Get index '%d' from stack error. '%s' expected but got '%s'", idx, LuaName<T0>::Of(), lua_typename(_L, n));
	return LuaGetter<T0>::Do(_L, idx);
}

template<typename T>
void LuaStack::Push(T t) {
	typedef typename LuaCleanType<T>::Type T0;
	LuaPusher<T0>::Do(_L, t);
}

//////////////////////////////////////////////////////////////////////////
/// LuaTable
//////////////////////////////////////////////////////////////////////////
template<typename T>
bool LuaTable::Is(int idx) {
	if (!IsValid()) return false;
	
	typedef typename LuaCleanType<T>::Type T0;

	lua_rawgeti(_L, LUA_REGISTRYINDEX, _ref);
	lua_rawgeti(_L, -1, idx);
	bool check = LuaType<T0>::Is(lua_type(_L, -1));
	lua_pop(_L, 2);
	return check;
}

template<typename T>
bool LuaTable::Is(const std::string & name) {
	if (!IsValid()) return false;

	typedef typename LuaCleanType<T>::Type T0;

	lua_rawgeti(_L, LUA_REGISTRYINDEX, _ref);
	lua_getfield(_L, -1, name.c_str());
	bool check = LuaType<T0>::Is(lua_type(_L, -1));
	lua_pop(_L, 2);
	return check;
}

template<typename T>
T LuaTable::Get(int idx) {
	if (!IsValid()) luaL_error(_L, "Try to index an invalid table!");
	
	typedef typename LuaCleanType<T>::Type T0;

	lua_rawgeti(_L, LUA_REGISTRYINDEX, _ref);
	lua_rawgeti(_L, -1, idx);

	int n = lua_type(_L, -1);
	if (!LuaType<T0>::Is(n)) {
		lua_pop(_L, 2);
		luaL_error(_L, "Get index '%d' of table. '%s' expected but got '%s'", idx, LuaName<T0>::Of(), lua_typename(_L, n));
	}

	T t = LuaGetter<T0>::Do(_L, -1);
	lua_pop(_L, 2);
	return t;
}

template<typename T>
T LuaTable::Get(const std::string & name) {
	if (!IsValid()) luaL_error(_L, "Try to index an invalid table!");

	typedef typename LuaCleanType<T>::Type T0;

	lua_rawgeti(_L, LUA_REGISTRYINDEX, _ref);
	lua_getfield(_L, -1, name.c_str());

	int n = lua_type(_L, -1);
	if (!LuaType<T0>::Is(n)) {
		lua_pop(_L, 2);
		luaL_error(_L, "Get index '%s' of table. '%s' expected but got '%s'", name.c_str(), LuaName<T0>::Of(), lua_typename(_L, n));
	}

	T t = LuaGetter<T0>::Do(_L, -1);
	lua_pop(_L, 2);
	return t;
}

template<typename T>
bool LuaTable::TryGet(int idx, T & out) {
	if (!IsValid()) return false;

	typedef typename LuaCleanType<T>::Type T0;

	lua_rawgeti(_L, LUA_REGISTRYINDEX, _ref);
	lua_rawgeti(_L, -1, idx);

	int n = lua_type(_L, -1);
	if (!LuaType<T0>::Is(n)) {
		lua_pop(_L, 2);
		return false;
	}

	out = LuaGetter<T0>::Do(_L, -1);
	lua_pop(_L, 2);
	return true;
}

template<typename T>
bool LuaTable::TryGet(const std::string & key, T & out) {
	if (!IsValid()) return false;

	typedef typename LuaCleanType<T>::Type T0;

	lua_rawgeti(_L, LUA_REGISTRYINDEX, _ref);
	lua_getfield(_L, -1, key.c_str());

	int n = lua_type(_L, -1);
	if (!LuaType<T0>::Is(n)) {
		lua_pop(_L, 2);
		return false;
	}

	out = LuaGetter<T0>::Do(_L, -1);
	lua_pop(_L, 2);
	return true;
}

template<typename T>
void LuaTable::Set(int idx, T t) {
	if (!IsValid()) luaL_error(_L, "Try to index an invalid table!");
	
	typedef typename LuaCleanType<T>::Type T0;

	lua_rawgeti(_L, LUA_REGISTRYINDEX, _ref);
	LuaPusher<T0>::Do(_L, t);
	lua_rawseti(_L, -2, idx);
	lua_pop(_L, 1);
}

template<typename T>
void LuaTable::Set(const std::string & name, T t) {
	if (!IsValid()) luaL_error(_L, "Try to index an invalid table!");

	typedef typename LuaCleanType<T>::Type T0;

	lua_rawgeti(_L, LUA_REGISTRYINDEX, _ref);
	LuaPusher<T0>::Do(_L, t);
	lua_setfield(_L, -2, name.c_str());
	lua_pop(_L, 1);
}

//////////////////////////////////////////////////////////////////////////
/// LuaNamespace
//////////////////////////////////////////////////////////////////////////
template<typename T>
LuaNamespace & LuaNamespace::Property(const std::string & name, T * ptr, bool read_only /* = true */) {
	int top = lua_gettop(_L);

	lua_rawgeti(_L, LUA_REGISTRYINDEX, _ref);
	lua_getfield(_L, -1, "__propget");
	if (!lua_istable(_L, -1)) {
		lua_settop(_L, top);
		luaL_error(_L, "Try to add property on a table without metatable!!!");
	}

	lua_pushstring(_L, name.c_str());
	lua_pushlightuserdata(_L, ptr);
	lua_pushcclosure(_L, &LuaProxy::Get<T>, 1);
	lua_rawset(_L, -3);
	lua_pop(_L, 1);

	lua_getfield(_L, -1, "__propset");
	if (!lua_istable(_L, -1)) {
		lua_settop(_L, top);
		luaL_error(_L, "Try to add property on a table without metatable!!!");
	}

	lua_pushstring(_L, name.c_str());
	if (read_only) {
		lua_pushvalue(_L, -1);
		lua_pushcclosure(_L, &LuaProxy::Readonly, 1);
	} else {
		lua_pushlightuserdata(_L, ptr);
		lua_pushcclosure(_L, &LuaProxy::Set<T>, 1);
	}
	lua_rawset(_L, -3);

	lua_settop(_L, top);
	return *this;
}

template<typename TG, typename TS>
LuaNamespace & LuaNamespace::Property(const std::string & name, TG (*getter)(), void (*setter)(TS)) {
	typedef TG(*Getter)();
	typedef void(*Setter)(TS);

	int top = lua_gettop(_L);

	lua_rawgeti(_L, LUA_REGISTRYINDEX, _ref);
	lua_getfield(_L, -1, "__propget");
	if (!lua_istable(_L, -1)) {
		lua_settop(_L, top);
		luaL_error(_L, "Try to add property on a table without metatable!!!");
	}

	lua_pushstring(_L, name.c_str());
	new (lua_newuserdata(_L, sizeof(Getter))) Getter(getter);
	lua_pushcclosure(_L, &LuaProxy::Getter<TG>, 1);
	lua_rawset(_L, -3);
	lua_pop(_L, 1);

	lua_getfield(_L, -1, "__propset");
	if (!lua_istable(_L, -1)) {
		lua_settop(_L, top);
		luaL_error(_L, "Try to add property on a table without metatable!!!");
	}

	lua_pushstring(_L, name.c_str());
	if (setter == nullptr) {
		lua_pushvalue(_L, -1);
		lua_pushcclosure(_L, &LuaProxy::Readonly, 1);
	} else {
		new (lua_newuserdata(_L, sizeof(Setter))) Setter(setter);
		lua_pushcclosure(_L, &LuaProxy::Setter<TS>, 1);
	}
	lua_rawset(_L, -3);

	lua_settop(_L, top);
	return *this;
}

//////////////////////////////////////////////////////////////////////////
/// LuaClass
//////////////////////////////////////////////////////////////////////////
template<class O> template<typename T>
LuaClass<O> & LuaClass<O>::Property(const std::string & name, T O::*ptr, bool read_only /* = true */) {
	typedef T O::*U;

	int top = lua_gettop(_L);

	lua_rawgeti(_L, LUA_REGISTRYINDEX, _ref);
	lua_getfield(_L, -1, "__propget");
	if (!lua_istable(_L, -1)) {
		lua_settop(_L, top);
		luaL_error(_L, "Try to add property on class without metatable!!!");
	}

	lua_pushstring(_L, name.c_str());
	new (lua_newuserdata(_L, sizeof(U))) U(ptr);
	lua_pushcclosure(_L, &LuaProxy::ClassGet<O, T>, 1);
	lua_rawset(_L, -3);
	lua_pop(_L, 1);

	lua_getfield(_L, -1, "__propset");
	if (!lua_istable(_L, -1)) {
		lua_settop(_L, top);
		luaL_error(_L, "Try to add property on class without metatable!!!");
	}

	lua_pushstring(_L, name.c_str());
	if (read_only) {
		lua_pushvalue(_L, -1);
		lua_pushcclosure(_L, &LuaProxy::Readonly, 1);
	} else {
		new (lua_newuserdata(_L, sizeof(U))) U(ptr);
		lua_pushcclosure(_L, &LuaProxy::ClassSet<O, T>, 1);
	}
	lua_rawset(_L, -3);

	lua_settop(_L, top);
	return *this;
}

template<class O> template<typename TG, typename TS>
LuaClass<O> & LuaClass<O>::Property(const std::string & name, TG (O::*getter)(), void (O::*setter)(TS)) {
	typedef TG(O::*ClassGetter)();
	typedef void (O::*ClassSetter)(TS);

	int top = lua_gettop(_L);

	lua_rawgeti(_L, LUA_REGISTRYINDEX, _ref);
	lua_getfield(_L, -1, "__propget");
	if (!lua_istable(_L, -1)) {
		lua_settop(_L, top);
		luaL_error(_L, "Try to add property on class without metatable!!!");
	}

	lua_pushstring(_L, name.c_str());
	new (lua_newuserdata(_L, sizeof(ClassGetter))) ClassGetter(getter);
	lua_pushcclosure(_L, &LuaProxy::ClassGetter<O, TG>, 1);
	lua_rawset(_L, -3);
	lua_pop(_L, 1);

	lua_getfield(_L, -1, "__propset");
	if (!lua_istable(_L, -1)) {
		lua_settop(_L, top);
		luaL_error(_L, "Try to add property on class without metatable!!!");
	}

	lua_pushstring(_L, name.c_str());
	if (setter == nullptr) {
		lua_pushvalue(_L, -1);
		lua_pushcclosure(_L, &LuaProxy::Readonly, 1);
	} else {
		new (lua_newuserdata(_L, sizeof(ClassSetter))) ClassSetter(setter);
		lua_pushcclosure(_L, &LuaProxy::ClassSetter<O, TS>, 1);
	}
	lua_rawset(_L, -3);

	lua_settop(_L, top);
	return *this;
}

template<class O> template<typename TG, typename TS>
LuaClass<O> & LuaClass<O>::Property(const std::string & name, TG (O::*getter)() const, void (O::*setter)(TS)) {
	typedef TG(O::*ClassGetter)() const;
	typedef void (O::*ClassSetter)(TS);

	int top = lua_gettop(_L);

	lua_rawgeti(_L, LUA_REGISTRYINDEX, _ref);
	lua_getfield(_L, -1, "__propget");
	if (!lua_istable(_L, -1)) {
		lua_settop(_L, top);
		luaL_error(_L, "Try to add property on class without metatable!!!");
	}

	lua_pushstring(_L, name.c_str());
	new (lua_newuserdata(_L, sizeof(ClassGetter))) ClassGetter(getter);
	lua_pushcclosure(_L, &LuaProxy::ClassConstGetter<O, TG>, 1);
	lua_rawset(_L, -3);
	lua_pop(_L, 1);

	lua_getfield(_L, -1, "__propset");
	if (!lua_istable(_L, -1)) {
		lua_settop(_L, top);
		luaL_error(_L, "Try to add property on class without metatable!!!");
	}

	lua_pushstring(_L, name.c_str());
	if (setter == nullptr) {
		lua_pushvalue(_L, -1);
		lua_pushcclosure(_L, &LuaProxy::Readonly, 1);
	} else {
		new (lua_newuserdata(_L, sizeof(ClassSetter))) ClassSetter(setter);
		lua_pushcclosure(_L, &LuaProxy::ClassSetter<O, TS>, 1);
	}
	lua_rawset(_L, -3);

	lua_settop(_L, top);
	return *this;
}

template<class O>
LuaClass<O> & LuaClass<O>::Method(const std::string & name, int (*func)(LuaStack &)) {
	lua_rawgeti(_L, LUA_REGISTRYINDEX, _ref);
	lua_pushstring(_L, name.c_str());
	lua_pushlightuserdata(_L, (void *)func);
	lua_pushcclosure(_L, &LuaProxy::ClassMethod<O>, 1);
	lua_rawset(_L, -3);
	lua_pop(_L, 1);
	return *this;
}

template<class O>
LuaClass<O> & LuaClass<O>::StaticMethod(const std::string & name, int (*func)(LuaStack &)) {
	lua_getglobal(_L, LuaClassInfo<O>::Name.c_str());
	int type = lua_type(_L, -1);
	lua_pushlstring(_L, name.c_str(), name.size());
	lua_pushlightuserdata(_L, (void*)func);
	lua_pushcclosure(_L, &LuaProxy::Method, 1);
	int top = lua_gettop(_L);
	lua_rawset(_L, -3);
	lua_pop(_L, 1);
	return *this;
}

//////////////////////////////////////////////////////////////////////////
/// LuaVM
//////////////////////////////////////////////////////////////////////////
template<typename T>
bool LuaVM::Is(const std::string & name) {
	typedef typename LuaCleanType<T>::Type T0;
	lua_getglobal(_L, name.c_str());
	bool check = LuaType<T0>::Is(lua_type(_L, -1));
	lua_pop(_L, 1);
	return check;
}

template<typename T>
T LuaVM::Get(const std::string & name) {
	typedef typename LuaCleanType<T>::Type T0;
	lua_getglobal(_L, name.c_str());
	int n = lua_type(_L, -1);
	if (!LuaType<T0>::Is(n)) {
		lua_pop(_L, 1);
		luaL_error(_L, "Get global '%s' error. '%s' expected but got '%s'", name.c_str(), LuaName<T0>::Of(), lua_typename(_L, n));
	}
	T t = LuaGetter<T0>::Do(_L, -1);
	lua_pop(_L, 1);
	return t;
}

template<typename T>
bool LuaVM::TryGet(const std::string & name, T & out) {
	typedef typename LuaCleanType<T>::Type T0;
	lua_getglobal(_L, name.c_str());
	int n = lua_type(_L, -1);
	if (!LuaType<T0>::Is(n)) {
		lua_pop(_L, 1);
		return false;
	}
	out = LuaGetter<T0>::Do(_L, -1);
	lua_pop(_L, 1);
	return true;
}

template<typename T>
void LuaVM::Set(const std::string & name, T t) {
	typedef typename LuaCleanType<T>::Type T0;
	LuaPusher<T0>::Do(_L, t);
	lua_setglobal(_L, name.c_str());
}

template<typename ... Args>
LuaStack LuaVM::Call(const std::string & table, const std::string & func, bool use_self, Args && ... args) {
	int top = lua_gettop(_L);

	lua_getglobal(_L, "debug");
	lua_getfield(_L, -1, "traceback");
	lua_remove(_L, -2);

	try {
		lua_getglobal(_L, table.c_str());
		if (!lua_istable(_L, -1)) {
			lua_settop(_L, top);
			luaL_error(_L, "Call %s.%s() error, %s is not a table!", table.c_str(), func.c_str(), table.c_str());
		}

		lua_getfield(_L, -1, func.c_str());
		int argc = sizeof ... (Args);
		if (use_self) {
			lua_pushvalue(_L, -2);
			lua_remove(_L, -3);
			argc++;
		} else {
			lua_remove(_L, -2);
		}

		if (!lua_isfunction(_L, -1)) {
			lua_settop(_L, top);
			luaL_error(_L, "Call %s.%s() error, %s is not a function!", table.c_str(), func.c_str(), func.c_str());
		}

		LuaMultiPusher::Do(_L, args...);

		if (lua_pcall(_L, argc, LUA_MULTRET, top + 1) != 0) {
			lua_remove(_L, top + 1);
			lua_error(_L);
		}

		lua_remove(_L, top + 1);
		return LuaStack(_L, lua_gettop(_L) - top);
	} catch (...) {
		lua_settop(_L, top);
		return LuaStack(_L, 0);
	}
}

template<class O>
LuaClass<O> LuaVM::Register(const std::string & class_name) {
	if (LuaClassInfo<O>::Used) {
		if (LuaClassInfo<O>::Name != class_name) {
			luaL_error(_L, "Class '%s' has already registered with key '%s'!!!", class_name.c_str(), LuaClassInfo<O>::Name.c_str());
		} else {
			return LuaClass<O>(_L, LuaClassInfo<O>::RefIdx);
		}
	}

	lua_getglobal(_L, class_name.c_str());
	if (!lua_isnil(_L, -1))
		luaL_error(_L, "Bad name for register class. '%s' alread exists!", class_name.c_str());
	lua_pop(_L, 1);
	lua_newtable(_L);

	if (luaL_newmetatable(_L, class_name.c_str()) == 0)
		luaL_error(_L, "Bad name for register class. '%s' alread exists!", class_name.c_str());
	
	lua_pushstring(_L, "__index");
	lua_pushcfunction(_L, &LuaProxy::ClassIndex<O>);
	lua_rawset(_L, -3);

	lua_pushstring(_L, "__newindex");
	lua_pushcfunction(_L, &LuaProxy::ClassNewIndex<O>);
	lua_rawset(_L, -3);

	lua_pushstring(_L, "__tostring");
	lua_pushcfunction(_L, &LuaProxy::ClassToString<O>);
	lua_rawset(_L, -3);

	lua_pushstring(_L, "__propget");
	lua_newtable(_L);
	lua_rawset(_L, -3);

	lua_pushstring(_L, "__propset");
	lua_newtable(_L);
	lua_rawset(_L, -3);

	int ref = luaL_ref(_L, LUA_REGISTRYINDEX);

	lua_pushstring(_L, "apis");
	lua_rawgeti(_L, LUA_REGISTRYINDEX, ref);
	lua_rawset(_L, -3);
	lua_setglobal(_L, class_name.c_str());

	LuaClassInfo<O>::Name	= class_name;
	LuaClassInfo<O>::Used	= true;
	LuaClassInfo<O>::RefIdx	= ref;

	return LuaClass<O>(_L, ref);
}

template<class Derived, class Base>
LuaClass<Derived> LuaVM::Register(const std::string & class_name) {
	if (!LuaClassInfo<Base>::Used) {
		luaL_error(_L, "You should register base class before derived['%s']!!!", class_name.c_str());
	}

	if (LuaClassInfo<Derived>::Used) {
		if (LuaClassInfo<Derived>::Name != class_name) {
			luaL_error(_L, "Class '%s' has already registered with key '%s'!!!", class_name.c_str(), LuaClassInfo<Derived>::Name.c_str());
		} else {
			return LuaClass<Derived>(_L, LuaClassInfo<Derived>::RefIdx);
		}
	}

	lua_getglobal(_L, class_name.c_str());
	if (!lua_isnil(_L, -1))
		luaL_error(_L, "Bad name for register class. '%s' alread exists!", class_name.c_str());
	lua_pop(_L, 1);
	lua_newtable(_L);

	if (luaL_newmetatable(_L, class_name.c_str()) == 0)
		luaL_error(_L, "Bad name for register class. '%s' alread exists!", class_name.c_str());
	
	lua_pushstring(_L, "__index");
	lua_pushcfunction(_L, &LuaProxy::ClassIndex<Derived>);
	lua_rawset(_L, -3);

	lua_pushstring(_L, "__newindex");
	lua_pushcfunction(_L, &LuaProxy::ClassNewIndex<Derived>);
	lua_rawset(_L, -3);

	lua_pushstring(_L, "__tostring");
	lua_pushcfunction(_L, &LuaProxy::ClassToString<Derived>);
	lua_rawset(_L, -3);

	lua_pushstring(_L, "__propget");
	lua_newtable(_L);
	lua_rawset(_L, -3);

	lua_pushstring(_L, "__propset");
	lua_newtable(_L);
	lua_rawset(_L, -3);

	lua_pushstring(_L, "__parent");
	luaL_getmetatable(_L, LuaClassInfo<Base>::Name.c_str());
	lua_rawset(_L, -3);

	int ref = luaL_ref(_L, LUA_REGISTRYINDEX);

	lua_pushstring(_L, "apis");
	lua_rawgeti(_L, LUA_REGISTRYINDEX, ref);
	lua_rawset(_L, -3);
	lua_setglobal(_L, class_name.c_str());

	LuaClassInfo<Derived>::Name		= class_name;
	LuaClassInfo<Derived>::Used		= true;
	LuaClassInfo<Derived>::RefIdx	= ref;

	return LuaClass<Derived>(_L, ref);
}

#endif//!	__SCRIPT_H_INCLUDED__
